package com.navinfo.platform.trip.common.arithmetic.manager;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.navinfo.platform.trip.common.arithmetic.data.DrivingData;
import com.navinfo.platform.trip.common.arithmetic.data.OuterStatisticData;
import com.navinfo.platform.trip.common.arithmetic.enums.ArithmeticStatus;
import com.navinfo.platform.trip.common.arithmetic.router.InvokeExceptionBody;
import com.navinfo.platform.trip.common.arithmetic.router.InvokeRoute;
import com.navinfo.platform.trip.common.config.ConfigMap;
import com.navinfo.platform.trip.common.util.CommonUtils;
import com.navinfo.platform.trip.common.util.GsonUtils;
import com.navinfo.platform.trip.common.util.JsonUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
 * 驾驶行为分析，计算管理器.
 * @author 水哥
 */
public class ArithmeticManager implements Serializable {
    private static Logger logger = LoggerFactory.getLogger(ArithmeticManager.class);
    private static long validate_total_num = 0;
    private static long validate_total_time = 0;

    /**
     * 中间数据.
     */
    private DrivingData drivingData;

    /**
     * 算法集合.
     */
    private List<IArithmetic> arithmeticList = new ArrayList<>();

    /**
     * 调用轨迹开关.
     */
    private boolean routerSwitch = "on".equalsIgnoreCase(ConfigMap.getString("router.switch"));

    /**
     * 异常报文打印最大长度.
     */
    private int exceptionMaxLength = ConfigMap.getInteger("router.exception.length.max") < 0 ? 0 : ConfigMap.getInteger("router.exception.length.max");

    /**
     * 设置中间数据.
     * @param drivingData 驾驶行为分析数据
     * @return ArithmeticManager
     */
    public ArithmeticManager setDrivingData(DrivingData drivingData) {
        this.drivingData = drivingData;
        return this;
    }

    /**
     * 添加算法.
     * @param iArithmetic 驾驶行为分析算法
     * @return ArithmeticManager
     */
    public ArithmeticManager addArithmetic(IArithmetic iArithmetic) {
        arithmeticList.add(iArithmetic);
        return this;
    }

    /**
     * 算法链中删除指定的算法链
     * @param delItemClazz
     * @return
     */
    public ArithmeticManager delArithmetic(Collection<Class<? extends IArithmetic>> delItemClazz) {
        for(int i=arithmeticList.size()-1; i>=0; i--){
            IArithmetic iArithmetic = arithmeticList.get(i);
            for(Class<? extends IArithmetic> delItemClass :delItemClazz){
                if(delItemClass.equals(iArithmetic.getClass())){
                    arithmeticList.remove(i);
                }
            }
        }
        return this;
    }

    /**
     * 获取链中算法个数
     * @return
     */
    public int getArithmeticCount(){
        return arithmeticList.size();
    }

    public List<IArithmetic> getArithmeticList() {
        return arithmeticList;
    }

    /**
     * 循环调用算法的计算方法
     */
    public ArithmeticManager calculate() {
        if (drivingData == null || CommonUtils.isCollectionEmpty(arithmeticList)) {
            return this;
        }

        //初始化调用轨迹参数
        InvokeRoute invokeRoute = new InvokeRoute();
        String message = null;
        if (routerSwitch) {
            try {
                message = JsonUtils.toJson(drivingData);
            } catch (JsonProcessingException e) {
                message = drivingData.toString();
            }
            //初始化每个算法为成功状态.
            for (IArithmetic iArithmetic : arithmeticList) {
                invokeRoute.getCallRouteMap().put(iArithmetic.getClass().getSimpleName(), ArithmeticStatus.NORMAL);
            }
            logger.error("原始commonData:{}", drivingData.getCommonData().toString());
        }

        //调用算法组，计算
        int exceptionCount = 0;
        long startTime = System.nanoTime();
        for (IArithmetic iArithmetic : arithmeticList) {
            try {
                iArithmetic.init(drivingData).calculate();
            } catch (Exception e) {
                Class<? extends IArithmetic> aClass = iArithmetic.getClass();
                logger.error("算法异常["+drivingData.getCommonData().getTid()+"] " + aClass.getSimpleName(), e);
                if (!routerSwitch) {
                    continue;
                }
                exceptionCount++;
                invokeRoute.getCallRouteMap().put(aClass.getSimpleName(), ArithmeticStatus.EXCEPTION);
                invokeRoute.getCallRouteExceptionMap().put(aClass.getSimpleName(), new InvokeExceptionBody(e.getMessage(), CommonUtils.getStackTrace(e, exceptionMaxLength)));
            }
        }

        //打印调用轨迹
        if (routerSwitch) {
            long endTime = System.nanoTime();
            int totalSize = arithmeticList.size();
            logger.error("");
            logger.error("算法调用轨迹:");
            logger.error("");
            logger.error("共调用: {}, 成功: {}, 失败: {}", totalSize, totalSize-exceptionCount, exceptionCount);
            logger.error("时长：{}ns", (endTime - startTime));
            validate_total_num++;
            validate_total_time += endTime-startTime;
            logger.error("次数：{}，平均时长：{}ns", validate_total_num, validate_total_time / validate_total_num);
            logger.error("-------------------------------------");
            if (exceptionCount == 0) {
                logger.error(" 全部成功 ");
            } else {
                logger.error(" 失败 ");
            }
            logger.error("-------------------------------------");
            if (exceptionCount > 0) {
                logger.error("原始报文：{}", message);
                logger.error("调用轨迹：:{}", GsonUtils.gsonString(invokeRoute));
                logger.error("-------------------------------------");
            }
        }

        return this;
    }

    /**
     * 循环调用算法的收集方法
     * @param statisticDataOut 统计数据.
     * @return ArithmeticManager arithmeticManager
     */
    public ArithmeticManager collect(OuterStatisticData statisticDataOut) {
        for (IArithmetic iArithmetic : arithmeticList) {
            try {
                iArithmetic.init(drivingData).collect(statisticDataOut);
            } catch (Exception e) {
                logger.error("算法collect异常:{}", iArithmetic.getClass(), e);
            }
        }
        return this;
    }

}
